package daobeauty.interceptor.returntype.handler;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

import daobeauty.interceptor.returntype.handler.impl.ArrayListReturnType;
import daobeauty.interceptor.returntype.handler.impl.ArrayReturnType;
import daobeauty.interceptor.returntype.handler.impl.BooleanReturnType;
import daobeauty.interceptor.returntype.handler.impl.ByteReturnType;
import daobeauty.interceptor.returntype.handler.impl.CollectionReturnType;
import daobeauty.interceptor.returntype.handler.impl.HashSetReturnType;
import daobeauty.interceptor.returntype.handler.impl.IntegerReturnType;
import daobeauty.interceptor.returntype.handler.impl.LinkedHashSetReturnType;
import daobeauty.interceptor.returntype.handler.impl.LinkedListReturnType;
import daobeauty.interceptor.returntype.handler.impl.LongReturnType;
import daobeauty.interceptor.returntype.handler.impl.ObjectReturnType;
import daobeauty.interceptor.returntype.handler.impl.ShortReturnType;
import daobeauty.interceptor.returntype.handler.impl.TreeSetReturnType;
import daobeauty.interceptor.returntype.handler.impl.VectorReturnType;
import daobeauty.interceptor.returntype.handler.impl.VoidReturnType;

/**
 * This is a singleton manager for all types of ReturnTypeHandlers. 
 * 
 * @author M. Serhat Cinar
 *
 */
public final class ReturnTypeHandlerManager {
	private static final HashMap<Class, ReturnTypeHandler> returnTypeHandlersByOutType = new HashMap<Class,ReturnTypeHandler>();
	// TODO check, ob es noch mehr typen gibt
	static{
		register(Object.class, new ObjectReturnType());
		register(Object[].class, new ArrayReturnType());
		register(List.class, new ArrayListReturnType());
		register(Vector.class, new VectorReturnType());
		register(ArrayList.class, new ArrayListReturnType());
		register(LinkedList.class, new LinkedListReturnType());
		register(Set.class, new LinkedHashSetReturnType());
		register(HashSet.class, new HashSetReturnType());
		register(LinkedHashSet.class, new LinkedHashSetReturnType());
		register(TreeSet.class, new TreeSetReturnType());
		register(SortedSet.class, new TreeSetReturnType());
		register(Collection.class, new CollectionReturnType());
		register(Long.class, new LongReturnType());
		register(Long.TYPE, new LongReturnType());
		register(Integer.class, new IntegerReturnType());
		register(Integer.TYPE, new IntegerReturnType());
		register(Short.class, new ShortReturnType());
		register(Short.TYPE, new ShortReturnType());
		register(Byte.class, new ByteReturnType());
		register(Byte.TYPE, new ByteReturnType());
		register(Boolean.class, new BooleanReturnType());
		register(Boolean.TYPE, new BooleanReturnType());
		register(null, new VoidReturnType());
	}
	
	private ReturnTypeHandlerManager(){}
	
	public static final void register(Class outType, ReturnTypeHandler returnTypeHandler){
		if (returnTypeHandler==null){
			throw new IllegalArgumentException("returnTypeHandler may not be null");
		}
		returnTypeHandlersByOutType.put(outType, returnTypeHandler);
	}
	
	/**
	 * Returns a ReturnTypeHandler for the given returntype of a method.
	 * Special threatments are applied in following order:<br/>
	 * List, SortedSet, Set, Collection, Object[] and Object.<br/>
	 * That means:<br/>
	 * First, it's tried to obtain a ReturnTypeHandler by the returntype itself.<br/>
	 * If there is none, the returntype is checked to be compatible with List.<br/>
	 * If it's not, the returntype is checked to be compatible with SortedSet etc...<br/>
	 * If it's compatible with any of the types, the appropriate ReturnTypeHandler is used. If not, at 
	 * least the Object ReturnTypeHandler is returned.
	 * 
	 * @param outType
	 * @return
	 */
	public static final ReturnTypeHandler getByOutType(Class outType){
		final ReturnTypeHandler returnTypeHandler = returnTypeHandlersByOutType.get(outType);
		if (returnTypeHandler==null){
			if (List.class.isAssignableFrom(outType)){
				return getByOutType(List.class);
			}
			if (SortedSet.class.isAssignableFrom(outType)){
				return getByOutType(SortedSet.class);
			}
			if (Set.class.isAssignableFrom(outType)){
				return getByOutType(Set.class);
			}
			if (Collection.class.isAssignableFrom(outType)){
				return getByOutType(Collection.class);
			}
			if (Object[].class.isAssignableFrom(outType)){
				return getByOutType(Object[].class);
			}
			return getByOutType(Object.class);
//			throw new IllegalArgumentException("no ReturnTypeHandler defined for outType "+(outType!=null?outType.getName():outType));
		}
		else{
			return returnTypeHandler;
		}
	}
}
